﻿#region COPYRIGHT© 2009-2012 Phillip Clark. All rights reserved.

// For licensing information see License.txt (MIT style licensing).

#endregion

using System;
using System.Diagnostics.Contracts;
using System.Net;
using System.Net.Sockets;
using FlitBit.Core;
using FlitBit.Proto.CodeContracts;

namespace FlitBit.Proto
{
	/// <summary>
	///   Interface for sending and receiving data on a network endpoint.
	/// </summary>
	[ContractClass(typeof(ContractForINetEndpoint))]
	public interface INetEndpoint : IInterrogateDisposable
	{
		/// <summary>
		///   Gets the underlying endpoint.
		/// </summary>
		IPEndPoint EndPoint { get; }

		/// <summary>
		///   Indicates whether the instance has been initialized with a socket.
		/// </summary>
		bool IsInitialized { get; }

		/// <summary>
		///   Accepts connections asynchronously
		/// </summary>
		/// <param name="endpoint"></param>
		/// <param name="callback"></param>
		void AcceptAsync(EndPoint endpoint, Action<Exception, OverlappedOpState> callback);

		/// <summary>
		///   Connects to an endpoint asynchronously.
		/// </summary>
		/// <param name="endpoint"></param>
		/// <param name="callback"></param>
		void ConnectAsync(EndPoint endpoint, Action<Exception, OverlappedOpState> callback);

		/// <summary>
		///   Initializes the endpoint with an external socket.
		/// </summary>
		/// <param name="socket"></param>
		/// <returns>the current instance</returns>
		void Initialize(Socket socket);

		/// <summary>
		///   Receives data from an endpoint.
		/// </summary>
		/// <param name="endpoint">reference to an endpoint.</param>
		/// <param name="buffer">buffer where the incomming data will be stored</param>
		/// <param name="offset">first byte within the buffer where data will be stored</param>
		/// <param name="size">number of bytes available in the buffer</param>
		/// <returns>number of bytes received</returns>
		int ReceiveFrom(ref EndPoint endpoint, byte[] buffer, int offset, int size);

		/// <summary>
		///   Receives data from an endpoint.
		/// </summary>
		/// <param name="endpoint">an endpoint.</param>
		/// <param name="buffer">buffer where the incomming data will be stored</param>
		/// <param name="offset">first byte within the buffer where data will be stored</param>
		/// <param name="size">number of bytes available in the buffer</param>
		/// <param name="callback">action called when the receive is complete</param>
		void ReceiveFromAsync(EndPoint endpoint, byte[] buffer, int offset, int size,
			Action<Exception, OverlappedOpState> callback);

		/// <summary>
		///   This method is used by the framework to release/reuse SocketAsyncEventArgs. You should not normally
		///   call this method..
		/// </summary>
		/// <param name="socketArgs"></param>
		void ReleaseAsyncEventArgs(SocketAsyncEventArgs socketArgs);

		/// <summary>
		///   Sends data to an endpoint.
		/// </summary>
		/// <param name="endpoint"></param>
		/// <param name="buffer"></param>
		void SendTo(EndPoint endpoint, byte[] buffer);

		/// <summary>
		///   Sends data to an endpoint.
		/// </summary>
		/// <param name="endpoint"></param>
		/// <param name="buffer"></param>
		/// <param name="size"></param>
		void SendTo(EndPoint endpoint, byte[] buffer, int size);

		/// <summary>
		///   Sends data to an endpoint.
		/// </summary>
		/// <param name="endpoint"></param>
		/// <param name="buffer"></param>
		/// <param name="offset"></param>
		/// <param name="size"></param>
		/// <param name="flags"></param>
		void SendTo(EndPoint endpoint, byte[] buffer, int offset, int size, SocketFlags flags);

		/// <summary>
		///   Asynchronously sends data to an endpoint.
		/// </summary>
		/// <param name="endpoint"></param>
		/// <param name="buffer"></param>
		/// <param name="size"></param>
		/// <param name="callback"></param>
		void SendToAsync(EndPoint endpoint, byte[] buffer, int size, Action<Exception, OverlappedOpState> callback);

		/// <summary>
		///   Asynchronously sends data to an endpoint.
		/// </summary>
		/// <param name="endpoint"></param>
		/// <param name="buffer"></param>
		/// <param name="offset"></param>
		/// <param name="size"></param>
		/// <param name="callback"></param>
		void SendToAsync(EndPoint endpoint, byte[] buffer, int offset, int size, Action<Exception, OverlappedOpState> callback);
	}

	namespace CodeContracts
	{
		/// <summary>
		///   CodeContracts Class for INetEndpoint
		/// </summary>
		[ContractClassFor(typeof(INetEndpoint))]
		internal abstract class ContractForINetEndpoint : INetEndpoint
		{
			#region INetEndpoint Members

			public bool IsDisposed { get { throw new NotImplementedException(); } }

			public bool IsInitialized { get { throw new NotImplementedException(); } }

			public void Initialize(Socket socket)
			{
				Contract.Requires<InvalidOperationException>(!IsInitialized);
				Contract.Requires<ArgumentNullException>(socket != null);
				Contract.Ensures(IsInitialized);
				throw new NotImplementedException();
			}

			public void AcceptAsync(EndPoint endpoint, Action<Exception, OverlappedOpState> callback)
			{
				Contract.Requires<InvalidOperationException>(IsInitialized);
				Contract.Requires<ObjectDisposedException>(!IsDisposed);
				Contract.Requires<ArgumentNullException>(endpoint != null);
				Contract.Requires<ArgumentNullException>(callback != null);

				throw new NotImplementedException();
			}

			public void ConnectAsync(EndPoint endpoint, Action<Exception, OverlappedOpState> callback)
			{
				Contract.Requires<InvalidOperationException>(IsInitialized);
				Contract.Requires<ObjectDisposedException>(!IsDisposed);
				Contract.Requires<ArgumentNullException>(endpoint != null);
				Contract.Requires<ArgumentNullException>(callback != null);

				throw new NotImplementedException();
			}

			public int ReceiveFrom(ref EndPoint endpoint, byte[] buffer, int offset, int size
				)
			{
				Contract.Requires<InvalidOperationException>(IsInitialized);
				Contract.Requires<ObjectDisposedException>(!IsDisposed);
				Contract.Requires<ArgumentNullException>(endpoint != null);
				Contract.Requires<ArgumentNullException>(buffer != null);
				Contract.Requires<ArgumentNullException>(buffer != null);
				Contract.Requires<ArgumentOutOfRangeException>(offset >= 0);
				Contract.Requires<ArgumentOutOfRangeException>(size >= 0);
				Contract.Requires<ArgumentOutOfRangeException>(size <= (buffer.Length - offset));

				throw new NotImplementedException();
			}

			public void ReceiveFromAsync(EndPoint endpoint, byte[] buffer, int offset, int size,
				Action<Exception, OverlappedOpState> callback)
			{
				Contract.Requires<InvalidOperationException>(IsInitialized);
				Contract.Requires<ObjectDisposedException>(!IsDisposed);
				Contract.Requires<ArgumentNullException>(endpoint != null);
				Contract.Requires<ArgumentNullException>(buffer != null);
				Contract.Requires<ArgumentNullException>(callback != null);
				Contract.Requires<ArgumentNullException>(buffer != null);
				Contract.Requires<ArgumentOutOfRangeException>(offset >= 0);
				Contract.Requires<ArgumentOutOfRangeException>(size >= 0);
				Contract.Requires<ArgumentOutOfRangeException>(size <= (buffer.Length - offset));

				throw new NotImplementedException();
			}

			public void SendTo(EndPoint endpoint, byte[] buffer)
			{
				Contract.Requires<InvalidOperationException>(IsInitialized);
				Contract.Requires<ObjectDisposedException>(!IsDisposed);
				Contract.Requires<ArgumentNullException>(endpoint != null);
				Contract.Requires<ArgumentNullException>(buffer != null);

				throw new NotImplementedException();
			}

			public void SendTo(EndPoint endpoint, byte[] buffer, int size)
			{
				Contract.Requires<InvalidOperationException>(IsInitialized);
				Contract.Requires<ObjectDisposedException>(!IsDisposed);
				Contract.Requires<ArgumentNullException>(endpoint != null);
				Contract.Requires<ArgumentNullException>(buffer != null);
				Contract.Requires<ArgumentOutOfRangeException>(size >= 0);
				Contract.Requires<ArgumentOutOfRangeException>(size <= (buffer.Length));

				throw new NotImplementedException();
			}

			public void SendTo(EndPoint endpoint, byte[] buffer, int offset, int size, SocketFlags flags)
			{
				Contract.Requires<InvalidOperationException>(IsInitialized);
				Contract.Requires<ObjectDisposedException>(!IsDisposed);
				Contract.Requires<ArgumentNullException>(endpoint != null);
				Contract.Requires<ArgumentNullException>(buffer != null);
				Contract.Requires<ArgumentOutOfRangeException>(offset >= 0);
				Contract.Requires<ArgumentOutOfRangeException>(size >= 0);
				Contract.Requires<ArgumentOutOfRangeException>(size <= (buffer.Length - offset));

				throw new NotImplementedException();
			}

			public void SendToAsync(EndPoint endpoint, byte[] buffer, int size, Action<Exception, OverlappedOpState> callback)
			{
				Contract.Requires<InvalidOperationException>(IsInitialized);
				Contract.Requires<ObjectDisposedException>(!IsDisposed);
				Contract.Requires<ArgumentNullException>(endpoint != null);
				Contract.Requires<ArgumentNullException>(buffer != null);
				Contract.Requires<ArgumentNullException>(callback != null);

				Contract.Requires<ArgumentNullException>(buffer != null);
				Contract.Requires<ArgumentOutOfRangeException>(size >= 0);
				Contract.Requires<ArgumentOutOfRangeException>(size <= (buffer.Length));

				throw new NotImplementedException();
			}

			public void SendToAsync(EndPoint endpoint, byte[] buffer, int offset, int size,
				Action<Exception, OverlappedOpState> callback)
			{
				Contract.Requires<InvalidOperationException>(IsInitialized);
				Contract.Requires<ObjectDisposedException>(!IsDisposed);
				Contract.Requires<ArgumentNullException>(endpoint != null);
				Contract.Requires<ArgumentNullException>(callback != null);

				Contract.Requires<ArgumentNullException>(buffer != null);
				Contract.Requires<ArgumentOutOfRangeException>(offset >= 0);
				Contract.Requires<ArgumentOutOfRangeException>(size >= 0);
				Contract.Requires<ArgumentOutOfRangeException>(size <= (buffer.Length - offset));

				throw new NotImplementedException();
			}

			/// <summary>
			///   Releases an async event args previously acquired.
			/// </summary>
			/// <param name="socketArgs"></param>
			public void ReleaseAsyncEventArgs(SocketAsyncEventArgs socketArgs)
			{
				Contract.Requires<ArgumentNullException>(socketArgs != null);
			}

			public IPEndPoint EndPoint { get { throw new NotImplementedException(); } }

			public void Dispose()
			{
				throw new NotImplementedException();
			}

			#endregion
		}
	}
}